<?php
// 应用公共文件


use Firebase\JWT\JWT;
use think\facade\Env;


/**
 * 当前是否为调试模式
 * @return bool
 */
function is_debug(): bool
{
    return (bool)Env::instance()->get('APP_DEBUG');
}


/**
 * 计算两个日期之间天数、小时数
 * @param string $startTime 开始时间
 * @param string $endTime 结束时间
 * @return array
 */
function countTimes(string $startTime, string $endTime): array
{
    $day = floor((strtotime($endTime) - strtotime($startTime)) / 86400);    //  相差天数
    $hour = floor((strtotime($endTime) - strtotime($startTime)) % 86400 / 3600);    //  相差小时数
    $minute = floor((strtotime($endTime) - strtotime($startTime)) % 86400 / 60);    //  相差分钟数
    $second = floor((strtotime($endTime) - strtotime($startTime)) % 86400 % 60);    //  相差秒数
    return [
        'day' => $day,
        'hour' => $hour,
        'minute' => $minute,
        'second' => $second,
    ];
}

/**
 * curl_get请求
 * $url 请求地址
 */
function curlGet($url, $type = 1)
{
    $header = array(
        'Accept: application/json',
    );
    $curl = curl_init();
    // 设置请求头
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    curl_setopt($curl, CURLOPT_TIMEOUT, 5000);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
    curl_setopt($curl, CURLOPT_URL, $url);
    if ($type == 1) {
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    }
    $res = curl_exec($curl);
    if ($res) {
        curl_close($curl);
        return $res;
    } else {
        $error = curl_errno($curl);
        curl_close($curl);
        return $error;
    }
}

/**
 * curl_post 请求
 * $url 请求链接
 * $postdata 请求数据
 * $type：1.json，2.array
 */
function curlPost($url, $postdata = [], $type = 1)
{
    $header = array(
        'Accept: application/json',
    );
    //初始化
    $curl = curl_init();
    //设置抓取的url
    curl_setopt($curl, CURLOPT_URL, $url);
    //设置头文件的信息作为数据流输出
    curl_setopt($curl, CURLOPT_HEADER, 0);
    //设置获取的信息以文件流的形式返回，而不是直接输出。
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    // 超时设置
    curl_setopt($curl, CURLOPT_TIMEOUT, 10);

    // 超时设置，以毫秒为单位
    // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

    // 设置请求头
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);

    //设置post方式提交
    curl_setopt($curl, CURLOPT_POST, 1);
    if ($type == 1) {
        curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
    } else {
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postdata));
    }
    //执行命令
    $data = curl_exec($curl);

    // 显示错误信息
    if (curl_error($curl)) {
        curl_close($curl);
        return "Error: " . curl_error($curl);
    } else {
        // 打印返回的内容
        curl_close($curl);
        return json_decode($data, true);

    }
}

/**
 * 成功返回信息
 * @param array|object $data 返回数据
 * @param int $status 状态码
 * @param string $msg 提示信息
 * @return \think\response\Json 返回数据
 */
function sucessMsg(array|object $data = [], int $status = 0, string $msg = '获取成功！', $code = 200): \think\response\Json
{
    return json([
        'code' => (int)$status,
        'msg' => $msg,
        'data' => $data
    ], $code);
}

/**
 * 失败返回信息
 * @param array $data 返回数据
 * @param int $status 状态码
 * @param string $msg 提示信息
 * @return \think\response\Json 返回数据
 */
function errMsg(string $msg = '获取失败！', int $status = -1, array $data = []): \think\response\Json
{
    return json([
        'code' => (int)$status,
        'msg' => $msg,
        'data' => $data
    ], 200);
}

/**
 * 返回随机字符串
 * length：字符串长度
 */
function getRandStr($length = 6)
{
    //字符组合
    $str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $len = strlen($str) - 1;
    $randstr = '';
    for ($i = 0; $i < $length; $i++) {
        $num = mt_rand(0, $len);
        $randstr .= $str[$num];
    }
    return $randstr;
}

/**
 * 生成密码
 * $password：密码
 * $salt：加密盐
 */
function makePassword($password, $salt)
{
    return md5(md5($password) . $salt);
}

/**
 * 验证密码
 * $password：密码
 * $salt：加密盐
 * $password_hash：用户密码
 */
function checkPassword($password, $member)
{
    if (empty($password) || empty($member)) {
        return false;
    }
    $inputPassword = makePassword($password, $member->salt);
    if ($inputPassword !== $member->password_hash) {
        return false;
    }
    return true;
}

/**
 * 生成验签
 * $uid 用户id
 * $key 这里是自定义的一个随机字串，应该写在config文件中的，解密时也会用，相当于加密中常用的盐  salt
 */
function signToken($uid, $type = 'api', $key = '!@#$%*&', $position = 1)
{
    $token = array(
        "iss" => $key,        //签发者 可以为空
        "aud" => '',          //面象的用户，可以为空
        "iat" => time(),      //签发时间
        "nbf" => time(),    //在什么时候jwt开始生效  （这里表示生成100秒后才生效）
        "exp" => time() + 60 * 60 * 24 * 7, //token 过期时间
        "data" => [           //记录的userid的信息，这里是自已添加上去的，如果有其它信息，可以再添加数组的键值对
            'uid' => $uid,
            'login_type' => $type,
            'position' => $position
        ]
    );
    $resToken = JWT::encode($token, $key, "HS256");  //根据参数生成了 token
    return $resToken;
}

/**
 * 验证token
 */
function checkToken($token, $key = '!@#$%*&')
{
    $status = array("code" => -1);
    try {
        JWT::$leeway = 60;//当前时间减去60，把时间留点余地
        $decoded = JWT::decode($token, new \Firebase\JWT\Key($key, 'HS256')); //HS256方式，这里要和签发的时候对应
        $arr = (array)$decoded;
        $res['code'] = 0;
        $res['data'] = $arr['data'];
        return $res;

    } catch (\Firebase\JWT\SignatureInvalidException $e) { //签名不正确
        $status['msg'] = "签名不正确";
        return $status;
    } catch (\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用
        $status['msg'] = "token未生效";
        return $status;
    } catch (\Firebase\JWT\ExpiredException $e) { // token过期
        $status['msg'] = "登录过期";
        return $status;
    } catch (\Exception $e) { //其他错误
        $status['msg'] = "缺少登录凭证";
        return $status;
    }
}

/**
 * 获取树结构
 * $arr 必须为数组
 * $pid 父id（上级id），默认为0
 */
function getTree($arr, $pid = 0, $level = 0)
{
    // 检验必须为数组
    if (!is_array($arr)) {
        return false;
    }
    $tree = [];
    foreach ($arr as $k => $v) {
        if ($v['parent_id'] == $pid) {
            $temp = $v;
            $temp['level'] = $level;
            $temp['children'] = getTree($arr, $v['id'], $level + 1);
            $tree[] = $temp;
        }
//        unset($arr[$k]);
    }
    return $tree;
}

/**
 * 获取树结构
 * $arr 必须为数组
 * $pid 父id（上级id），默认为0
 */
function getMenuTree(array $arr, $parent_id = 0, $level = 0): array
{
    // 检验必须为数组
    if (!is_array($arr)) return false;
    $tree = [];
    foreach ($arr as $k => $v) {
        if ($v['parent_id'] == $parent_id) {
            $temp = $v;
            $temp['title'] = $temp['meta']['title'];
            $temp['icon'] = $temp['meta']['icon'];
            $temp['route'] = $temp['name'];
            $temp['children'] = getMenuTree($arr, $v['id'], $level + 1);
            unset($temp['parent_id'], $temp['meta'], $temp['name']);
            $tree[] = $temp;
        }
    }
    return $tree;
}

/**
 * 正则验证
 * $param 为变量
 * $type 为类型有以下：
 * number：整数验证
 * mobile：手机号验证
 * emial：邮箱验证
 * card：身份证验证
 * user：合法用户，只能包含字母、汉字、数字、下划线
 */
function preg($param, $type = 'number')
{
    switch ($type) {
        //  判断是否为大于0的整数
        case 'number':
            if (preg_match("/^[1-9][1-9]*$/", $param)) {
                return true;
            } else {
                return false;
            }
            break;
        //  判断是否为正确手机号
        case 'mobile':
            if (preg_match("/^[1-9][0-9]*$/", $param)) {
                return true;
            } else {
                return false;
            }
            break;
        //  判断是否为正确邮箱号
        case 'email':
            if (preg_match("/^\w+[-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/", $param)) {
                return true;
            } else {
                return false;
            }
            break;
        //  判断是否为正确身份证号
        case 'card':
            if (preg_match("/^\d{15}|\d{18}$/", $param)) {
                return true;
            } else {
                return false;
            }
            break;
        //  判断是否为和法名称
        case 'user':
            if (preg_match("/^[\x{4e00}-\x{9fa5}A-Za-z0-9_]+$/u", $param)) {
                return true;
            } else {
                return false;
            }
            break;
        //  判断是否为合法金额
        case 'money':
            if (preg_match('/^[1-9]\d*|^[1-9]\d*.\d+[1-9]$/', $param)) {
                return true;
            } else {
                return false;
            }
            break;
    }
}


/**
 * 过滤掉emoji表情
 */
function filterEmoji($str)
{
    $str = preg_replace_callback('/./u',
        function (array $match) {
            return strlen($match[0]) >= 4 ? '' : $match[0];
        },
        $str);
    return $str;
}

/**
 * 拖拽式排序
 * $modle 需要操作的数据库模型
 * $ids 数据的id集合
 * $oldIndex 原始位置，从0开始算
 * $newIndex 要拖动的位置
 * $sort  排序方式，要与列表保持一致
 */
function setDragSort($model, $ids = [], $oldIndex = 0, $newIndex = 1, $sort = 'sort desc, id asc')
{
    //  定于返回信息
    $res = [
        'code' => 0,
        'msg' => '操作成功!'
    ];
    if (!is_array($ids)) {
        $res['code'] = -1;
        $res['msg'] = '数据格式错误!';
        return $res;
    }
    $list = $model->field('id,sort')->whereIn('id', $ids)->order($sort)->select()->toArray(); //  查询相关数据
    if (empty($list)) {
        $res['code'] = -1;
        $res['msg'] = '数据不存在!';
        return $res;
    }
    //  定义id集合
    $idArr = [];
    //  定义排序集合
    $sortArr = [];
    foreach ($list as $item) {
        $idArr[] = $item['id'];
        $sortArr[] = $item['sort'];
    }
    //  记录要拖动的id
    $oldValue = $idArr[$oldIndex];
    //  删除这个要拖动的id
    unset($idArr[$oldIndex]);
    //  插入新的位置，并自动移位
    array_splice($idArr, $newIndex, 0, $oldValue);
    //  重新设置排序
    $newArr = [];
    for ($i = 0; $i < count($idArr); $i++) {
        $newArr[$i]['id'] = $idArr[$i];
        $newArr[$i]['sort'] = $sortArr[$i] == 1 ? $idArr[$i] : $sortArr[$i];
    }
    $result = $model->saveAll($newArr);
    if (!$result) {
        $res['code'] = -1;
        $res['msg'] = '操作失败!';
        return $res;
    }
    return $res;
}

/**
 * 获取最大排序值
 * $model 需要操作的数据库模型
 */
function getMaxSort($model)
{
    $max = 1;
    $res = $model->order('sort desc')->value('sort');
    if (!empty($res)) $max = $res + 1;
    return $max;
}

/**
 * 获取redis缓存
 * $key：键值
 */
function getRedisVal($key)
{
    return Cache::store('redis')->get($key);
}

/**
 * 存储redis缓存
 * $key：键值
 * $val：缓存内容
 * $time：缓存时间，单位秒
 */
function setRedisVal($key, $val, $time = 24 * 3600)
{
    return Cache::store('redis')->set($key, $val, $time);
}

/**
 * 删除redis缓存
 * $key：键值
 */
function delRedisVal($key)
{
    return Cache::store('redis')->delete($key);
}


/**
 * 清空redis缓存
 */
function clearRedisVal()
{
    return Cache::store('redis')->clear();
}

/**
 * redis自增
 * $key：键值
 */
function incRedisVal($key)
{
    return Cache::store('redis')->inc($key);
}


/**
 * 把格林威治时间转为标准时间
 */
function standard($standardTime)
{
    return date("Y-m-d H:i:s", strtotime($standardTime));
}

/**
 * 格林威治时间转为时间戳
 */
function unixToGmt($UnixTime)
{
    return strtotime(date("Y-m-d H:i:s", strtotime($UnixTime)));
}

/**
 * 自定义排序，从大到小排序，冒泡排序
 * $arr 数组，$key键值
 */
function maxToMin($arr, $key)
{
    $len = count($arr); // 数组长度
    for ($i = 0; $i < $len - 1; $i++) {
        for ($j = 0; $j < $len - 1 - $i; $j++) {
            if ($arr[$j][$key] < $arr[$j + 1][$key]) { // 相邻两个值作比较，选出小的那个值，交换位置，然后继续往后做比较直到数组最后一个值
                $temp = $arr[$j];
                $arr[$j] = $arr[$j + 1];
                $arr[$j + 1] = $temp;
            }
        }
    }
    return $arr;
}

/**
 * 递归数据,递归成多维数组，childs存储下级，生产环境中请把返回值进行缓存，这样只查库一次，可以在新增和更新的方法处再调用这个函数更新缓存
 * @return array $list 一个表示层级关系的多维数组
 */
function digui($id)
{
    $userModel = new UserModel();
    $children = $userModel->where('id', $id)->value('children');//2,3
    $list = explode(',', $children);//[2,3]
    foreach ($list as $key => $item) {
        if ($userModel->where('id', $item)->value('children')) {
            $children = $children . ',' . digui($item);
        }
    }
    return $children;
}